// Copyright (c) 2008 Matthew Johnson
// Licensed under the MIT License (see license.txt)
package actionml.core {
	import actionml.Global;
	
	public class StringMethods {
		public static function initialize() {
			Global.initializeExtension(StringMethods, String);
		}
		public static var instanceMethods:Object = {
			compileExpression: function() {
				var expression:Array = [];
				var currentGroup:Array = expression;
				for each (var token:String in this.split(/\s*(\+|-|\*|\/|\(|\))\s*/)) {
					if (token == ")") {
						currentGroup = currentGroup.parent;
					} else if (token) {
						if (token == "(") {
							var newGroup:Array = [];
							newGroup.parent = currentGroup;
							currentGroup[currentGroup.length] = newGroup;
							currentGroup = newGroup;
						} else if (token) {
							if (["+", "-", "*", "/"].contains(token)) {
								currentGroup[currentGroup.length] = token;
							} else if (!isNaN(Number(token))) {
								currentGroup[currentGroup.length] = Number(token);
							} else if (token.match(/^\w+(\.\w+)*$/)) {
								currentGroup[currentGroup.length] = token;
							} else {
								trace("compile expression error " + token)
								// TODO : trace an error
							}
						}
					}
				}
				return expression;	
			},
			evaluateExpression: function(context:Object) {
				return context.getValueForKeypath(this);
			},
			toCamelCase: function() {
				if (this.contains("-") || this.contains("_")) {
					return this.toLowerCase().replace(/^[_\-]/, "").replace(/(_|-)([a-z])/g, function() {
						return arguments[2].toUpperCase();
					});
				} else {
					return this.replace(/^[a-zA-Z]/, function() {
						return arguments[0].toLowerCase();
					});
				}
			},
			toCapitalCase: function() {
				return this.replace(/(^|\s)(\w)/g, function() {
					return arguments[1] + arguments[2].toUpperCase();
				});
			},
			contains: function(s:String) {
				return this.indexOf(s) > -1;
			},
			startsWith: function(s:String) {
				return this.indexOf(s) == 0;
			},
			endsWith: function(s:String) {
				return s.length <= this.length && this.indexOf(s) == (this.length - s.length);
			},
			withoutPrefix: function(s:String) {
				return this.slice(s.length, s.length + 1).toLowerCase() + this.slice(s.length + 1);
			},
			lastPart: function(separator:String=".") {
				return this.slice(this.lastIndexOf(separator) + 1);
			},
			withoutLastPart: function(separator:String=".") {
				var i:int = this.lastIndexOf(separator);
				if (i > 0) {
					return this.slice(0, i);
				} else {
					return "";
				}
			},
			countOf: function(s:String) {
				if (s == "") {
					return 0;
				}
				var i:int = 0;
				var startIndex:int = startIndex = this.indexOf(s);				
				while(startIndex > -1) {
					i++;
					startIndex = this.indexOf(s, startIndex + 1);
				}
				return i;
			},
			getIndentationInfo: function(indentationInfo:Object = null, errorPrefix:String = "") {
				indentationInfo ||= {};
				indentationInfo.level ||= 0;
				indentationInfo.spacesPerTab ||= 0;
				indentationInfo.maximumLevelIncrease ||= 0;
				indentationInfo.maximumLevelDecrease ||= 0;
				var newIndentationInfo = { 
					level: 0,
					spacesPerTab: indentationInfo.spacesPerTab,
					maximumLevelIncrease: indentationInfo.maximumLevelIncrease,
					maximumLevelDecrease: indentationInfo.maximumLevelDecrease
				};
				var indentation:String = this.match(/^\s*/)[0];
				var spaceCount:int = indentation.countOf(" ");
				var tabCount:int = indentation.countOf("\t");
				
				if (spaceCount > 0 && indentationInfo.spacesPerTab == 0) {
					if (tabCount > indentationInfo.level) {
						throw new Error(errorPrefix + "The first line with an occurence of space indentation must increase the indentation level by 1.  This line increases the indentation level from " + indentationInfo.level + " to " + tabCount + " via tabs alone and also includes " + spaceCount + " space(s) that further increase then indentation level.");
					}
					
					newIndentationInfo.spacesPerTab = spaceCount / (indentationInfo.level - tabCount + 1);
					indentationInfo.spacesPerTab = newIndentationInfo.spacesPerTab;
				}
				if (indentationInfo.spacesPerTab > 0 && spaceCount % indentationInfo.spacesPerTab != 0) {
					throw new Error(errorPrefix + "Expected spaces per tab was " + indentationInfo.spacesPerTab + ".  " + spaceCount + " space(s) were found and this number cannot be converted to a tab count without a remainder of spaces " + spaceCount % indentationInfo.spacesPerTab + " space(s).");
				}
				
				newIndentationInfo.level = tabCount;
				if (indentationInfo.spacesPerTab > 0) {
					newIndentationInfo.level += spaceCount / indentationInfo.spacesPerTab;
				}
				
				if (indentationInfo.maximumLevelIncrease > 0 && newIndentationInfo.level > indentationInfo.level + indentationInfo.maximumLevelIncrease) {
					throw new Error(errorPrefix + "Indentation can only increase by " + indentationInfo.maximumLevelIncrease + " level(s) at a time.  An invalid indentation increase from " + indentationInfo.level + " to " + newIndentationInfo.level + " exists.");
				}
				if (indentationInfo.maximumLevelDecrease > 0 && newIndentationInfo.level < indentationInfo.level - indentationInfo.maximumLevelDecrease) {
					throw new Error(errorPrefix + "Indentation can only decrease by " + indentationInfo.maximumLevelDecrease + " level(s) at a time.  An invalid indentation decrease from " + indentationInfo.level + " to " + newIndentationInfo.level + " exists.");
				}
				
				return newIndentationInfo;
			}
		};
	}
}